<template>
  <view class="exp-box full-w full-h">
    <view class="exp-box__preview-box w-full">
      <scroll-view
        class="exp-box__preview-box__scroll"
        :scroll-x="true"
        :scroll-left="previewScrollLeft"
        :scroll-with-animation="true"
      >
        <view class="h-full flex flex-items-center">
          <view class="exp-box__preview-box__text">
            {{ formatExpressionResult }}
          </view>
        </view>
      </scroll-view>
    </view>
    <view class="exp-box__input-box w-full">
      <scroll-view
        class="exp-box__input-box__scroll"
        :scroll-x="true"
        :scroll-left="scrollLeft"
        :scroll-with-animation="true"
      >
        <view class="exp-box__input-box__content h-full flex flex-items-center">
          <view
            class="exp-box__input-box__content__item"
            :class="item.class"
            v-for="item in curDisplayInputList"
            :key="item.timestamp"
          >
            <view
              v-if="item.isCursor"
              class="exp-box__input-box__content__item_cursor__content"
            ></view>
            <view
              v-if="!item.isCursor"
              class="exp-box__input-box__content__item__click-area left"
              @click="handleInputTextClick('left', item.realIndex)"
            ></view>
            <view
              v-if="!item.isCursor"
              class="exp-box__input-box__content__item__click-area right"
              @click="handleInputTextClick('right', item.realIndex)"
            ></view>
            {{ item.val }}
          </view>
        </view>
      </scroll-view>
      <view
        class="btn-del"
        hover-class="btn_hover"
        :hover-stay-time="100"
        @click="handleBtnDelClick"
      >
        <wd-icon name="keyboard-delete" size="26px" color="#919191"></wd-icon>
      </view>
    </view>
  </view>
</template>

<script lang="ts" setup>
import type { PropType } from 'vue'
import { useCalculatorStore } from '@/store/calculator'

const calculatorStore = useCalculatorStore()

const props = defineProps({
  inputData: {
    type: Object as PropType<Expression.InputData>,
    required: true,
    default: () => ({
      val: '',
      type: 'num',
      timestamp: 0,
    }),
  },
})

const emits = defineEmits(['btnDelClick'])

const inputTextList = ref([])
const scrollLeft = ref(0)
const cursor = ref({
  index: 0,
  timestamp: 0,
})

const previewScrollLeft = ref(0)

const expressionList = ref([])
const expressionResult = ref<string | number>(0)

const formatInputTextList = computed(() => {
  return inputTextList.value.map((item, index) => {
    return {
      ...item,
      realIndex: index,
    }
  })
})

const curDisplayInputList = computed(() => {
  const newArr = formatInputTextList.value.map((item) => ({ ...item, isCursor: false }))

  // 插入光标
  newArr.splice(cursor.value.index, 0, {
    val: 'cursor',
    type: 'cursor',
    timestamp: cursor.value.timestamp,
    isCursor: true,
    realIndex: -1,
  })

  const result = newArr.map((item) => {
    return {
      ...item,
      class: item.isCursor ? 'exp-box__input-box__content__item_cursor' : '',
    }
  })
  console.log(
    '%c [ curDisplayInputList ]-63',
    'font-size:13px; background:pink; color:#bf2c9f;',
    cursor.value,
    result,
  )
  return result
})

const formatExpressionResult = computed(() => {
  let result = expressionResult.value
  // 使用正则 结果中如果出现'e+',替换为'E'
  result = result.toString().replace(/e\+/g, 'E')

  // 给结果添加千分位
  const resultArr = result.split('.')
  const intPart = resultArr[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  result = resultArr.length > 1 ? `${intPart}.${resultArr[1]}` : intPart

  return result
})

const handleBtnDelClick = () => {
  emits('btnDelClick')

  const deleteIndex = cursor.value.index
  const afterList = inputTextList.value.slice(deleteIndex)

  if (afterList.length === 0) return

  console.log(
    '%c [ handleBtnDelClick ]-112',
    'font-size:13px; background:pink; color:#bf2c9f;',
    afterList.length,
  )
  inputTextList.value.splice(deleteIndex, 1)

  calculatorStore.vibrateShort()
}

// 重置光标
const resetCursor = () => {
  cursor.value.index = 0
  cursor.value.timestamp = 0
}

const setInput = (val: string, type: string, timestamp: number) => {
  const enableOption = {
    enable: true, // 是否允许输入
    insertIndex: cursor.value.index, // 最终插入位置
  }

  const reverserCursorIndex = inputTextList.value.length - cursor.value.index

  const expressionListLen = expressionList.value.length

  const numTextList = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
  const zeroTextList = ['0', '00']
  const operatorTextList = ['+', '-', '×', '÷']

  const getFindIndex = (type) => {
    return expressionListLen
      ? expressionList.value.findIndex((item) => {
          if (item.type !== type) return false
          if (item.indexRange.length > 1) {
            return (
              reverserCursorIndex >= item.indexRange[0] && reverserCursorIndex <= item.indexRange[1]
            )
          } else {
            return reverserCursorIndex === item.indexRange[0]
          }
        })
      : -1
  }

  const curFindNumIndex = getFindIndex('num')
  const curFindNumItem = expressionList.value[curFindNumIndex] || null

  const curFindOperatorIndex = getFindIndex('operator')
  const curFindOperatorItem = expressionList.value[curFindOperatorIndex] || null

  console.log(
    '%c [ setInput ]-183',
    'font-size:13px; background:pink; color:#bf2c9f;',
    reverserCursorIndex,
    curFindNumItem,
  )

  const strList = curFindNumItem?.strList || []
  const strListLen = strList.length
  const isFirstZero = strList[0] === '0'
  const isFloat = curFindNumItem?.isFloat
  const floatIntText = strList.join('').split('.')[0]
  const isFloatIntOnlyOneZero = floatIntText === '0'
  const hasMultipleZero = floatIntText.split('').filter((item) => item === '0').length > 1
  const floatPointIndex = curFindNumItem?.indexRange[0] + strList.indexOf('.') + 1

  if (zeroTextList.includes(val)) {
    if (!curFindNumItem) {
      // 当前位置没有数字，输入0
      val = '0'
    } else {
      if (isFloat) {
        // 当前项为小数
        console.log(
          '%c [ isFloat ]-220',
          'font-size:13px; background:pink; color:#bf2c9f;',
          isFloat,
          isFloatIntOnlyOneZero,
          floatPointIndex,
          reverserCursorIndex,
          curFindNumItem.indexRange[0],
          floatIntText,
          floatIntText.length,
          isFirstZero,
          hasMultipleZero,
        )
        if (isFirstZero) {
          // 判断当前项的首字母是否为0
          if (hasMultipleZero) {
            // 当前项的整数部分有多个0，不可输入0（针对del操作）例如 000.33
            return
          }
          if (
            isFloatIntOnlyOneZero &&
            reverserCursorIndex >= curFindNumItem.indexRange[0] &&
            reverserCursorIndex < floatPointIndex
          ) {
            // 如果小数点前只有一个0，且光标位于整数以及小数点之间，则不可输入0，例如 (光标位置)0.3 、0(光标位置).3
            return
          }
        } else {
          if (floatIntText.length === 0 && reverserCursorIndex < floatPointIndex) {
            // 当前项的整数部分为空，且光标位于小数点之前，则可输入0，例如 (光标位置).3 -> 0(光标位置).3
            val = '0'
          }
          if (floatIntText.length >= 1 && reverserCursorIndex <= curFindNumItem.indexRange[0]) {
            // 当前项的整数部分不为空，且光标位于整数部分之前，不可输入0，避免出现 010.33 021.11 等情况
            return
          }
        }
      } else {
        // 当前项为整数
        if (
          strListLen >= 1 &&
          (isFirstZero || reverserCursorIndex <= curFindNumItem.indexRange[0])
        ) {
          // 1. 整数位数大于等于1位，且首字母为0 例如 01 011
          // 2. 整数位数大于等于1位，且光标位于整数部分之前 例如 (光标位置)100 0(光标位置)1
          return
        }
      }
    }
  }

  if (numTextList.includes(val)) {
    if (curFindNumItem) {
      if (
        strListLen >= 1 &&
        isFirstZero &&
        reverserCursorIndex === curFindNumItem.indexRange[0] + 1
      ) {
        inputTextList.value.splice(enableOption.insertIndex, 1, {
          val,
          type,
          timestamp,
        })
        return
      }
    }
  }

  if (operatorTextList.includes(val)) {
    // 禁止输入 + × ÷ 作为开头
    if (
      (reverserCursorIndex === 0 ||
        (curFindOperatorItem && curFindOperatorItem.indexRange[0] === 0)) &&
      val !== '-'
    ) {
      return
    }

    // 1. 运算符后输入运算符，用新运算符替换旧运算符
    // 2. '.'后输入运算符，用新运算符替换'.'
    if (
      curFindOperatorItem ||
      (curFindNumItem &&
        curFindNumItem.indexRange[1] === reverserCursorIndex &&
        strList[strListLen - 1] === '.')
    ) {
      inputTextList.value.splice(enableOption.insertIndex, 1, {
        val,
        type,
        timestamp,
      })
      return
    }
  }

  if (val === '.') {
    if (curFindNumItem) {
      if (isFloat) {
        return
      } else {
        if (reverserCursorIndex === curFindNumItem.indexRange[0]) {
          val = '0.'
        }
      }
    }
    if (!curFindNumItem) {
      val = '0.'
    }
  }

  if (enableOption.enable) {
    let valList: any = val.length > 1 ? val.split('') : [val]
    valList = valList.map((text) => {
      return {
        val: text,
        type,
        timestamp,
      }
    })

    valList.forEach((item) => {
      inputTextList.value.splice(enableOption.insertIndex, 0, item)
    })
  }
}

const handleInputTextList = () => {
  const { val, type, timestamp } = props.inputData
  if (!val) return

  if (val === 'AC') {
    return (inputTextList.value = [])
  }

  if (val === '=') {
    return handleEqual()
  }

  console.log(
    '%c [ expressionList ]-169',
    'font-size:13px; background:pink; color:#bf2c9f;',
    expressionList.value,
  )

  setInput(val, type, timestamp)

  scrollToLeft()
}

const handleEqual = () => {
  console.log(
    '%c [ handleEqual ]-238',
    'font-size:13px; background:pink; color:#bf2c9f;',
    inputTextList.value,
  )
  const inputTextListLen = inputTextList.value.length
  const firstItem = inputTextList.value[0] || {}
  if (inputTextListLen === 0) return
  if (inputTextListLen === 1 && firstItem?.type === 'operator') return
  if (firstItem?.type === 'operator' || firstItem?.val === '.') {
    inputTextList.value.shift()
  }

  calculatorStore.addHistory({
    expression:
      inputTextList.value
        .reverse()
        .map((item) => item.val)
        .join('') + '=',
    result: formatExpressionResult.value.toString(),
  })

  inputTextList.value = []
}

const handleInputTextClick = (direction: 'left' | 'right', index: number) => {
  const newIndex = direction === 'left' ? index + 1 : index
  if (newIndex === cursor.value.index) return
  cursor.value.index = newIndex
  cursor.value.timestamp = Date.now()
  console.log(
    '%c [ handleInputTextClick-index ]-101',
    'font-size:13px; background:pink; color:#bf2c9f;',
    direction,
    index,
  )
  console.log(
    '%c [ handleInputTextClick-cursorIndex ]-101',
    'font-size:13px; background:pink; color:#bf2c9f;',
    cursor.value.index,
    inputTextList.value.length - cursor.value.index,
  )
}

const scrollToLeft = () => {
  scrollLeft.value = 1
  nextTick(() => {
    scrollLeft.value = 0
  })
}

const previewScrollToLeft = () => {
  previewScrollLeft.value = 1
  nextTick(() => {
    previewScrollLeft.value = 0
  })
}

const getExpressionList = () => {
  const reverseList = inputTextList.value.map((item) => item).reverse()
  const reverseListLen = reverseList.length
  const result = reverseList.reduce(
    (prev, cur) => {
      if (cur.type === 'num') {
        prev.tempStr += cur.val
      }

      if ((prev.pos === reverseListLen || cur.type === 'operator') && prev.tempStr) {
        const numPos = cur.type === 'operator' ? prev.pos - 1 : prev.pos
        const strToNum = Number(prev.tempStr)
        const strList = prev.tempStr.split('')
        const isFloat = strList.includes('.')
        prev.expressionList.push({
          val: strToNum,
          type: 'num',
          isFloat,
          strList,
          indexRange: [numPos - prev.tempStr.length, numPos],
        })
        prev.tempStr = ''
      }

      if (cur.type === 'operator') {
        prev.expressionList.push({
          val: cur.val,
          type: 'operator',
          indexRange: [prev.pos - 1, prev.pos],
        })
      }

      prev.pos += 1

      return prev
    },
    {
      expressionList: [],
      pos: 1,
      tempStr: '',
    },
  )

  expressionList.value = result.expressionList
  console.log(
    '%c [ getExpressionList ]-427',
    'font-size:13px; background:pink; color:#bf2c9f;',
    result.expressionList,
  )
}

// 对表达式进行计算
const getExpressionResult = () => {
  const expressionListLen = expressionList.value.length
  if (expressionListLen === 0) return (expressionResult.value = '')

  // 1. 先计算乘除，并将结果与加减运算符一起存入新数组，得到加减运算符和数字的新数组
  const addSubMulList = expressionList.value.reduce(
    (prev, cur) => {
      if (cur.type === 'operator') {
        if (['+', '-'].includes(cur.val)) {
          prev.list.push({
            val: cur.val,
            type: 'operator',
          })
          prev.tempSum = 0
          prev.tempOperator = ''
        } else if (['×', '÷'].includes(cur.val)) {
          prev.tempOperator = cur.val
        }
      } else {
        if (!prev.tempSum) {
          prev.list.push({
            val: prev.tempSum,
            type: 'num',
          })
        }

        if (prev.tempOperator) {
          if (prev.tempOperator === '×') {
            prev.tempSum = prev.tempSum * cur.val
          } else if (prev.tempOperator === '÷') {
            prev.tempSum = prev.tempSum / cur.val
          }
          prev.tempOperator = ''
        } else {
          prev.tempSum = cur.val
        }
        prev.list[prev.list.length - 1].val = prev.tempSum
      }

      return prev
    },
    {
      list: [],
      tempSum: 0,
      tempOperator: '',
    },
  ).list

  // 2. 计算加减
  const result = addSubMulList.reduce(
    (prev, cur) => {
      if (cur.type === 'operator') {
        prev.operator = cur.val
      }

      if (cur.type === 'num') {
        if (prev.operator === '+') {
          prev.sum += cur.val
        } else if (prev.operator === '-') {
          prev.sum += cur.val * -1
        } else {
          prev.sum = cur.val
        }
        prev.operator = ''
      }

      return prev
    },
    {
      sum: 0,
      operator: '',
    },
  ).sum

  expressionResult.value = result

  previewScrollToLeft()
}

watch(
  () => props.inputData,
  () => {
    handleInputTextList()
  },
  {
    deep: true,
    immediate: true,
  },
)
watch(
  () => inputTextList.value,
  (newVal) => {
    if (newVal?.length === 0) {
      resetCursor()
    }
    // 转化获取表达式
    getExpressionList()
    // 计算表达式
    getExpressionResult()
  },
  {
    deep: true,
  },
)
</script>

<style lang="scss" scoped>
.exp-box {
  position: relative;

  .exp-box__preview-box {
    height: 120rpx;

    .exp-box__preview-box__scroll {
      width: 100%;
      height: 100%;
      white-space: nowrap;
      transform: rotate(0.5turn);

      ::-webkit-scrollbar {
        display: none;
        width: 0 !important;
        height: 0 !important;
        appearance: none;
        background: transparent;
      }

      .exp-box__preview-box__text {
        height: 100%;
        font-size: 92rpx;
        font-weight: bold;
        line-height: 120rpx;
        color: #4d4d4d;
        white-space: nowrap;
        transform: rotate(0.5turn);
      }
    }
  }

  .exp-box__input-box {
    position: relative;
    height: 90rpx;
    padding-right: 80rpx;

    .exp-box__input-box__scroll {
      position: absolute;
      top: 0;
      left: 0;
      width: calc(100% - 80rpx);
      height: 100%;
      white-space: nowrap;
      transform: rotate(0.5turn);

      ::-webkit-scrollbar {
        display: none;
        width: 0 !important;
        height: 0 !important;
        appearance: none;
        background: transparent;
      }

      .exp-box__input-box__content {
        flex-wrap: nowrap;

        .exp-box__input-box__content__item {
          position: relative;
          z-index: 2;
          font-size: 46rpx;
          color: #4d4d4d;
          white-space: nowrap;
          transform: rotate(-0.5turn);

          &.exp-box__input-box__content__item_cursor {
            position: relative;
            z-index: 1;
            width: 2px;
            height: 100%;
            color: transparent;

            @keyframes ani-cursor {
              0%,
              100% {
                opacity: 1;
              }

              50% {
                opacity: 0;
              }
            }
            // overflow: hidden; // 加了这个，在内容超过scroll可视宽度后，会导致光标不显示
            .exp-box__input-box__content__item_cursor__content {
              position: absolute;
              top: 50%;
              left: 0;
              width: 100%;
              height: 70%;
              overflow: hidden;
              color: transparent;
              transform: translateY(-50%);

              &::before {
                position: absolute;
                top: 0;
                left: 0;
                width: 200%;
                height: 100%;
                content: '';
                background: #a559dd;
                animation: ani-cursor 1s steps(1, start) 0.2s infinite;
              }
            }
          }

          .exp-box__input-box__content__item__click-area {
            position: absolute;
            width: 50%;
            height: 100%;

            &.left {
              left: 0;
              // background-color: rgba(125, 125, 125, 0.5);
            }

            &.right {
              right: 0;
              // background-color: rgba(150, 50, 125, 0.5);
            }
          }
        }
      }
    }

    .btn-del {
      position: absolute;
      top: 50%;
      right: 0;
      transform: translateY(-50%);
    }
  }
}

.btn_hover {
  opacity: 0.5;
}
</style>
